unit ORM.Types.CNPJ_CPF;

interface

type
  TCNPJ_CPF = record
  private
    FValue: string;
    FValorReferencia: IInterface;
    function GetValue: string;
    function ChecaCNPJ(const pCNPJ: string): boolean;
    function ChecaCPF(const pCPF: string): boolean;
    function FormataCPFCNPJ(pValue: string): string;
    constructor Create(const pValue : string);
  public
    property Value : string read GetValue;
    function IsEmpty : Boolean;
    function IsValid : Boolean;
    function GetFormatValue : string;
    function Equals(const pOther : TCNPJ_CPF) : Boolean;
    procedure Clear;

    class operator Implicit(const pValue: string): TCNPJ_CPF;
    class operator Implicit(const pValue: TCNPJ_CPF): string;
    class operator Add(pLeft, pRigth : TCNPJ_CPF) : TCNPJ_CPF;
    class operator Equal(pLeft, pRigth : TCNPJ_CPF) : Boolean;
    class operator NotEqual(pLeft, pRigth : TCNPJ_CPF) : Boolean;
  end;

implementation

uses
  SysUtils, System.Generics.Defaults, ORM.Utils, ORM.Types.Base;

{ TCNPJ_CPF }

constructor TCNPJ_CPF.Create(const pValue: string);
begin
  FValue := SomenteNumeros(pValue);
  FValorReferencia := CValorReferencia;
end;

class operator TCNPJ_CPF.Equal(pLeft, pRigth: TCNPJ_CPF): Boolean;
begin
  Result := pLeft.Equals(pRigth);
end;

function TCNPJ_CPF.Equals(const pOther: TCNPJ_CPF): Boolean;
begin
  if not pOther.IsEmpty then
    Result := TEqualityComparer<TCNPJ_CPF>.Default.Equals(Value, pOther.Value)
  else
    Result := IsEmpty = pOther.IsEmpty;
end;

class operator TCNPJ_CPF.Implicit(const pValue: TCNPJ_CPF): string;
begin
  Result := pValue.Value;
end;

function TCNPJ_CPF.FormataCPFCNPJ(pValue: string): string;
Var
  vTam, xx : Integer;
  vDoc : String;
begin
  vTam := Length(pValue);
  For xx := 1 To vTam Do
  begin
    If (Copy(pValue,xx,1) <> '.') And (Copy(pValue,xx,1) <> '-') And (Copy(pValue,xx,1) <> '/') Then
      vDoc := vDoc + Copy(pValue,xx,1);
  end;

  pValue := vDoc;
  vTam := Length(pValue);
  vDoc := '';
  vDoc := '';
  For xx := 1 To vTam Do
  begin
    vDoc := vDoc + Copy(pValue,xx,1);
    If vTam = 11 Then
    begin
      If (xx in [3,6]) Then vDoc := vDoc + '.';
      If xx = 9 Then vDoc := vDoc + '-';
    end;

    If vTam = 14 Then
    begin
      If (xx in [2,5]) Then vDoc := vDoc + '.';
      If xx = 8 Then vDoc := vDoc + '/';
      If xx = 12 Then vDoc := vDoc + '-';
    end;
  end;
  Result := vDoc;
end;

function TCNPJ_CPF.GetFormatValue: string;
begin
  Result := FormataCPFCNPJ(FValue);
end;

function TCNPJ_CPF.GetValue: string;
begin
  Result := FValue;
end;

class operator TCNPJ_CPF.Implicit(const pValue: string): TCNPJ_CPF;
begin
  Result := TCNPJ_CPF.Create(pValue);
end;

function TCNPJ_CPF.IsEmpty: Boolean;
begin
  Result := Length(FValorReferencia) = 0;
end;

class operator TCNPJ_CPF.Add(pLeft, pRigth: TCNPJ_CPF): TCNPJ_CPF;
begin
  if not (pLeft.IsEmpty) and not (pRigth.IsEmpty) then
    Result := pLeft.Value + pRigth.Value
  else
    Result.Clear;
end;

function TCNPJ_CPF.ChecaCNPJ(const pCNPJ: string): boolean;
var
  Numero: array [1 .. 14] of byte;
  I: byte;
  Calculo, Calculo1: Integer;
  Digito: string;
begin
  Result := false;
  if (Empty(pCNPJ)) or (Length(pCNPJ) <> 14) then
    exit;
  if not IsNumero(pCNPJ) then
    exit;

  for I := 1 to 12 do
    Numero[I] := CharToInt(pCNPJ[I]);
  Calculo := 0;
  for I := 1 to 04 do
    Inc(Calculo, Numero[I] * (6 - I));
  for I := 5 to 12 do
    Inc(Calculo, Numero[I] * (14 - I));
  Numero[13] := 11 - (Calculo mod 11);
  if Numero[13] >= 10 then
    Numero[13] := 0;

  Calculo1 := 0;
  for I := 1 to 05 do
    Inc(Calculo1, Numero[I] * (7 - I));
  for I := 6 to 13 do
    Inc(Calculo1, Numero[I] * (15 - I));
  Numero[14] := 11 - (Calculo1 mod 11);
  if Numero[14] >= 10 then
    Numero[14] := 0;
  Digito := IntToChar(Numero[13]) + IntToChar(Numero[14]);
  if Digito <> Copy(pCNPJ, 13, 2) then
    exit;
  Result := true;
end;

function TCNPJ_CPF.ChecaCPF(const pCPF: string): boolean;
var
  b, I, soma: Integer;
  Digito: SmallInt;
begin
  Result := false;
  If (Trim(pCPF) = '.') Then
    exit;
  if (Empty(pCPF)) or (Length(pCPF) <> 11) then
    exit;
  if not IsNumero(pCPF) then
    exit;
  b := 11;
  soma := 0;
  for I := 1 to 10 do
  begin
    Inc(soma, CharToInt(pCPF[I]) * b);
    Dec(b);
  end;
  Digito := 11 - (soma mod 11);
  if Digito >= 10 then
    Digito := 0;
  Result := (IntToChar(Digito) = pCPF[11]);
end;

procedure TCNPJ_CPF.Clear;
begin
  FValue := EmptyStr;
  FValorReferencia := EmptyStr;
end;

function TCNPJ_CPF.IsValid: Boolean;
begin
  Result := False;
  if Length(FValue) < 14 then
    Result := ChecaCPF(FValue)
  else
    Result := ChecaCNPJ(FValue);
end;


class operator TCNPJ_CPF.NotEqual(pLeft, pRigth: TCNPJ_CPF): Boolean;
begin
  Result := not pLeft.Equals(pRigth);
end;

end.
